package slide_window

import (
	"container/list"
	"ratelimit"
	"sync"
	"time"
)

type SlideWindowLimit struct {
	queue    *list.List
	duration time.Duration
	limit    int64
	mutex    sync.Mutex
}

func New(duration time.Duration, limit int64) ratelimit.Limiter {
	limiter := &SlideWindowLimit{queue: list.New(), duration: duration, limit: limit}

	return limiter
}

func (s *SlideWindowLimit) Allow() bool {
	now := time.Now().UnixNano()
	boundary := now - int64(s.duration/time.Nanosecond)

	s.mutex.Lock()

	// fast path
	if s.queue.Len() < int(s.limit) {
		s.queue.PushBack(now)
		s.mutex.Unlock()
		return true
	}

	// slow path
	timestamp := s.queue.Front()
	if timestamp != nil && timestamp.Value.(int64) < boundary {
		s.queue.Remove(timestamp)
		timestamp = s.queue.Front()
	}

	length := s.queue.Len()
	s.mutex.Unlock()

	if length >= int(s.limit) {
		return false
	}

	s.queue.PushBack(now)

	return false
}
